using DotNetCommon.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace DotNetCommon.Expressions
{
    internal class ExpressionNode
    {
        /// <summary>
        /// 当前节点的表达式
        /// </summary>
        internal Expression Expression { get; set; }
        /// <summary>
        /// 表达式类型
        /// </summary>
        internal ExpressionType NodeType => Expression.NodeType;
        /// <summary>
        /// 子节点
        /// </summary>
        internal List<ExpressionNode> Children { get; set; }
        /// <summary>
        /// 父节点
        /// </summary>
        internal ExpressionNode Parent { get; set; }

        /// <summary>
        /// 当期节点有更新
        /// </summary>
        internal bool _updateRequest { get; set; }

        /// <summary>
        /// 当前节点是否有更新
        /// </summary>
        internal bool NeedUpdate => Children.IsNullOrEmpty() ? _updateRequest : Children.Any(i => i.NeedUpdate);


        internal List<ParameterExpression> _parameters;
        /// <summary>
        /// 源参数列表
        /// </summary>
        internal List<ParameterExpression> Parameters => Parent == null ? _parameters : Parent.Parameters;
        /// <summary>
        /// 当前节点的完整唯一描述(基于此实现缓存)
        /// </summary>
        internal string FullMarkString { get; set; }
        /// <summary>
        /// 当前节点是否是源参数
        /// </summary>
        internal bool IsParameter { get; set; }
        /// <summary>
        /// 当前节点是否含源参数
        /// </summary>
        internal bool HasParameter => IsParameter ? true : Children.IsNotNullOrEmpty() && Children.Any(i => i.HasParameter);
        /// <summary>
        /// 当前节点是否是局部变量
        /// </summary>
        internal bool IsLocalVariable { get; set; }

        private static ConcurrentDictionary<string, Delegate> _caches = new ConcurrentDictionary<string, Delegate>();

        internal object Reduce()
        {
            var dic = new Dictionary<Expression, ParameterExpression>();
            var parameters = new List<ParameterExpression>();
            var localVars = new List<object>();
            foreach (var item in Children)
            {
                visit(item);
            }

            var dele = _caches.GetOrAdd(FullMarkString, key =>
            {
                var lambda = LambdaExpression.Lambda(Expression, parameters.ToArray());
                var dele = lambda.Compile();
                return dele;
            });
            return dele.DynamicInvoke(localVars.ToArray());

            //从子节点中提取出局部变量 变成参数
            void visit(ExpressionNode node)
            {
                if (node.Expression == null) return;
                if (node.Expression.NodeType == ExpressionType.MemberAccess)
                {
                    if (node.Children.FirstOrDefault().IsLocalVariable)
                    {
                        var member = node.Expression as MemberExpression;
                        var val = (member.Expression as ConstantExpression).Value;
                        if (!localVars.Contains(val))
                        {
                            localVars.Add(val);
                            var para = dic.GetOrAdd(member.Expression, () => Expression.Parameter(member.Expression.Type, "i"));
                            parameters.Add(para);
                        }
                        return;
                    }
                    visit(node.Children[0]);
                }
                else
                {
                    node.Children.ForEach(x => visit(x));
                }
            }
        }
    }
}
